package com.kamistoat.ruoyi.ruoyimodulessystem.aspect;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kamistoat.ruoyi.ruoyiapisystem.domain.SysUser;
import com.kamistoat.ruoyi.ruoyiapisystem.entity.SysDeptEntity;
import com.kamistoat.ruoyi.ruoyiapisystem.entity.SysRoleDeptEntity;
import com.kamistoat.ruoyi.ruoyiapisystem.entity.SysRoleEntity;
import com.kamistoat.ruoyi.ruoyiapisystem.model.LoginUser;
import com.kamistoat.ruoyi.ruoyicommoncore.constant.UserConstants;
import com.kamistoat.ruoyi.ruoyicommoncore.utils.StringUtils;
import com.kamistoat.ruoyi.ruoyicommondatascope.annotation.DataScope;
import com.kamistoat.ruoyi.ruoyicommonsecurity.utils.SecurityUtils;
import com.kamistoat.ruoyi.ruoyimodulessystem.service.SysDeptService;
import com.kamistoat.ruoyi.ruoyimodulessystem.service.SysRoleDeptService;
import com.kamistoat.ruoyi.ruoyimodulessystem.service.SysRoleService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * 使用切面处理 @DataScope注解，在整体上解决数据权限过滤。
 * 注意。由于原项目不是用的mybatis-plus，所以这个切面只是再用String去拼接，那么独立出来做也是毫无问题的。但是现在想要用 Mybatis-plus，考虑将这个切面进行移植。
 * 思路是:
 * 在LoginUser中添加一个Map属性字段，用于保存各种额外参数。
 * 切片拦截使用 @DataScope 接口方法。对于一个请求，只需要在该请求的整个链路中的第一次调用时执行
 */
@Aspect
@Component
public class DataScopeAspect {

    @Autowired
    SysRoleDeptService sysRoleDeptService;
    @Autowired
    SysDeptService sysDeptService;
    @Autowired
    SysRoleService sysRoleService;


    /**
     * 全部数据权限
     */
    public static final String DATA_SCOPE_ALL = "1";

    /**
     * 自定数据权限
     */
    public static final String DATA_SCOPE_CUSTOM = "2";

    /**
     * 本人所在部门数据权限
     */
    public static final String DATA_SCOPE_DEPT = "3";

    /**
     * 本人所在部门及以下数据权限
     */
    public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";

    /**
     * 仅本人数据权限
     */
    public static final String DATA_SCOPE_SELF = "5";

    /**
     * 数据权限过滤关键字
     */
    public static final String DATA_SCOPE = "dataScope";

    public static final String DEPT_IDS = UserConstants.DEPT_IDS;

    public static final String USER_IDS = UserConstants.USER_IDS;

    /**
     * 在切点拦截的方法前处理。
     *
     * @param joinPoint           切点
     * @param dataScopeAnnotation 注解
     * @throws Exception
     */
    @Before("@annotation(dataScopeAnnotation)")
    public void doBefore(JoinPoint joinPoint, DataScope dataScopeAnnotation) throws Exception {
        // 从ThreadLocal中获取当前登录用户令牌
        LoginUser currentLoginUser = SecurityUtils.getLoginUser();
        if (StringUtils.isNotNull(currentLoginUser)) {
            // 如果在**当前线程**中已经在之前执行过数据权限过滤了(即两个Allowed列表只要有一个非空)，则这里直接跳过。
            if (StringUtils.isNotEmpty(currentLoginUser.getAllowedDeptIds()) &&
                    StringUtils.isNotEmpty(currentLoginUser.getAllowedUserIds())) {

                SysUser currentSysUser = currentLoginUser.getSysUser();
                Set<Long> deptIds = new LinkedHashSet<>();
                Set<Long> userIds = new LinkedHashSet<>();

                // 如果当前用户不是超级管理员，则设置数据权限过滤
                if (StringUtils.isNotNull(currentSysUser) && !SecurityUtils.curLoginUserIsAdmin()) {
                    // 一个登录对象可能有多个角色身份，这些身份各自的数据权限过滤应该以OR相连
                    List<SysRoleEntity> roleList =
                            sysRoleService.list(new QueryWrapper<SysRoleEntity>().in("role_id", currentLoginUser.getRoleIds()));

                    for (SysRoleEntity sysRoleEntity : roleList) {
                        String dataScope = sysRoleEntity.getDataScope();
                        // 拥有全部权限，直接return。则此时两个Allowed数组和超级管理员时一样，都是空
                        if (dataScope.equals(DATA_SCOPE_ALL)) {
                            return;
                        }
                        // 自定义权限，则需要在 sys_role_dept 表中进行查询
                        if (dataScope.equals(DATA_SCOPE_CUSTOM)) {
                            List<SysRoleDeptEntity> sysRoleDeptList = sysRoleDeptService.list(
                                    new QueryWrapper<SysRoleDeptEntity>().select("dept_id").eq("role_id", sysRoleEntity.getRoleId()));
                            for (SysRoleDeptEntity sysRoleDeptEntity : sysRoleDeptList) {
                                deptIds.add(sysRoleDeptEntity.getDeptId());
                            }
                        }
                        // 仅本人所在部门
                        if (dataScope.equals(DATA_SCOPE_DEPT)) {
                            deptIds.add(currentSysUser.getDeptId());
                        }
                        // 本人所在部门及以下
                        if (dataScope.equals(DATA_SCOPE_DEPT_AND_CHILD)) {
                            deptIds.add(currentSysUser.getDeptId());
                            List<SysDeptEntity> sysDeptEntityList = sysDeptService.list(
                                    new QueryWrapper<SysDeptEntity>()
                                            .select("dept_id")
                                            .apply("find_in_set({0}, ancestors)", currentSysUser.getDeptId().toString()));
                            for (SysDeptEntity sysDeptEntity : sysDeptEntityList) {
                                deptIds.add(sysDeptEntity.getDeptId());
                            }
                        }
                        // 仅本人数据.该角色只允许访问自己，连自己的部门都不允许。此时添加角色id
                        if (dataScope.equals(DATA_SCOPE_SELF)) {
                            userIds.add(currentSysUser.getUserId());
                        }
                    }
                }

                currentLoginUser.setAllowedDeptIds(new ArrayList<>(deptIds));
                currentLoginUser.setAllowedUserIds(new ArrayList<>(userIds));
            }
        }
    }
}
